package com.tencent.sr.iris.activity.service.activity.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.tencent.sr.iris.activity.common.enums.AwardRecordIssueStatusEnum;
import com.tencent.sr.iris.activity.common.util.ShardingIdUtil;
import com.tencent.sr.iris.activity.dependency.qufa.GroupTaskDependency;
import com.tencent.sr.iris.activity.interfaces.dto.AwardInfoDTO;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityIdEnum;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityTaskIdEnum;
import com.tencent.sr.iris.activity.interfaces.request.acticity.ActivityGiftPopupRequest;
import com.tencent.sr.iris.activity.interfaces.request.acticity.AwardConfirmRequest;
import com.tencent.sr.iris.activity.interfaces.request.acticity.AwardRecordRequest;
import com.tencent.sr.iris.activity.interfaces.request.task.SpecialAreaQualificationRequest;
import com.tencent.sr.iris.activity.interfaces.response.activity.ActivityGiftPopupResponse;
import com.tencent.sr.iris.activity.interfaces.response.group.task.SpecialAreaQualificationResponse;
import com.tencent.sr.iris.activity.repository.entity.TIrisActivityAwardRecordDO;
import com.tencent.sr.iris.activity.repository.entity.TIrisUserTaskRecordDO;
import com.tencent.sr.iris.activity.repository.mapper.TIrisActivityAwardRecordMapper;
import com.tencent.sr.iris.activity.repository.mapper.example.TIrisActivityAwardRecordExample;
import com.tencent.sr.iris.activity.service.activity.ActivityCommonService;
import com.tencent.sr.iris.activity.service.activity.EsDataOperateService;
import com.tencent.sr.iris.activity.service.activity.IrisActivityAwardRecordService;
import com.tencent.sr.iris.activity.service.activity.IrisActivityTaskService;
import com.tencent.sr.iris.activity.service.config.ActivityConfig;
import com.tencent.sr.iris.activity.service.config.RegisterGiftPopupConfig;
import com.tencent.sr.iris.activity.service.config.ResponseMockConfig;
import com.tencent.sr.iris.activity.service.constant.ActivityConfigConstants;
import com.tencent.sr.iris.activity.service.constant.ActivityRedisKey;
import com.tencent.sr.iris.activity.service.event.util.ActivityCommonUtilService;
import com.tencent.sr.iris.user.core.service.interfaces.enums.TutorChannelSettingEnum;
import com.tencent.sr.iris.user.core.service.interfaces.enums.UserLevelEnum;
import com.tencent.sr.iris.user.core.service.interfaces.response.user.IrisQueryUsersInTagsResponse;
import com.tencent.sr.rmall.material.interfaces.enums.EYesNo;
import com.tencent.sr.rmall.springbootstarter.redis.RedisClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class IrisActivityAwardRecordServiceImpl extends EsDataOperateService implements IrisActivityAwardRecordService {

    @Resource
    public ActivityConfig activityConfig;

    @Resource
    private RedisClient redisClient;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private IrisActivityTaskService irisActivityTaskService;
    @Resource
    private TIrisActivityAwardRecordMapper activityAwardRecordMapper;
    @Resource
    private RegisterGiftPopupConfig registerGiftPopupConfig;

    @Resource
    private ActivityCommonService activityCommonService;

    @Resource
    private ActivityCommonUtilService activityCommonUtilService;
    
    @Resource
    private GroupTaskDependency groupTaskDependency;

    @Value("${env.es.award.record.index.name:user-activity-award-record}")
    private String indexName;

    @Resource
    private ResponseMockConfig responseMockConfig;

    @Override
    protected String[] indices() {
        return new String[] {indexName};
    }

    @Override
    public ActivityGiftPopupResponse popup(ActivityGiftPopupRequest request) {
        String uid = request.getUid();
        Long registGiftTimestamp = activityCommonUtilService.getRegistGiftEndTime(uid);
        boolean newPop = System.currentTimeMillis() >= registGiftTimestamp;

        if (request.getQueryNewRegistGift()) {
            ActivityGiftPopupResponse res = new ActivityGiftPopupResponse();
            res.setFlag(false);
            res.setUid(uid);
            res.setNewRegistGift(newPop);
            res.setNewRegistGiftTimestamp(registGiftTimestamp);
            return res;
        }

        if (newPop) {
            ActivityGiftPopupResponse newPopup = newPopup(request);
            newPopup.setNewRegistGift(true);
            newPopup.setNewRegistGiftTimestamp(registGiftTimestamp);
            return newPopup;
        }

        ActivityGiftPopupResponse oldPopup = oldPopup(request);
        oldPopup.setNewRegistGift(false);
        oldPopup.setNewRegistGiftTimestamp(registGiftTimestamp);
        return oldPopup;
    }

    private ActivityGiftPopupResponse newPopup(ActivityGiftPopupRequest request) {
        String uid = request.getUid();
        ActivityGiftPopupResponse res = new ActivityGiftPopupResponse();
        res.setFlag(false);
        res.setUid(uid);
        RLock lock = null;
        try {
            lock = redissonClient.getLock(ActivityRedisKey.getUserAwardPopupLockKey(uid));
            if (!lock.tryLock(1L, TimeUnit.SECONDS)) {
                log.info("newPopup 获取分布式锁失败, uid:{}", uid);
                return res;
            }

            //判断今天是否已经弹窗过
            boolean isPoppedToday = checkPoppedToday(uid, request.getTaskId());
            if (isPoppedToday && !activityConfig.isIgnoreTodayPop()) {
                log.info("newPopup 今天已经弹窗过，忽略请求，uid:{}", uid);
                return res;
            }

            String levelId = activityCommonUtilService.queryUserLevelId(uid);
            if (!Objects.equals(UserLevelEnum.TRAINEE.getValue(), levelId)) {
                log.info("newPopup 当前用户{}等级{}非会员，忽略请求，uid:{}", uid,
                        Optional.ofNullable(UserLevelEnum.getByValue(levelId))
                                .map(UserLevelEnum::getDesc).orElse(null));
                return res;
            }

            SpecialAreaQualificationRequest qualificationRequest = new SpecialAreaQualificationRequest();
            qualificationRequest.setUid(uid);
            SpecialAreaQualificationResponse qualification = groupTaskDependency.specialAreaQualification(qualificationRequest);
            Boolean hasQualification = Optional.ofNullable(qualification)
                    .map(SpecialAreaQualificationResponse::getHasQualification).orElse(Boolean.FALSE);
            String specialAreaId = Optional.ofNullable(qualification)
                    .map(SpecialAreaQualificationResponse::getSpecialAreaId).orElse(StrUtil.EMPTY);
            res.setFlag(hasQualification);
            res.setSpecialAreaId(specialAreaId);
            if(hasQualification) {
                setPopTagToday(uid, request.getTaskId());
            }
            log.info("newPopup 弹窗成功 uid:{}，response{}", uid, JSON.toJSONString(res));
        } catch (Exception e) {
            log.error("newPopup 执行异常, uid:{}", uid, e);
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return res;
    }

    private ActivityGiftPopupResponse oldPopup(ActivityGiftPopupRequest request) {
        String uid = request.getUid();

        ActivityGiftPopupResponse res = new ActivityGiftPopupResponse();
        res.setFlag(false);
        res.setUid(uid);
        RLock lock = null;
        try {

            lock = redissonClient.getLock(ActivityRedisKey.getUserAwardPopupLockKey(uid));
            if (!lock.tryLock(1L, TimeUnit.SECONDS)) {
                log.info("popup 获取分布式锁失败, uid:{}", uid);
                return res;
            }

            //场景值：自营转自购省（ZI_YING），胖鹅析出（PANG_E）,不触发邀请有礼发券
            IrisQueryUsersInTagsResponse userTags = activityCommonUtilService.queryUserTags(uid);
            if(userTags == null || CollectionUtil.isEmpty(userTags.getTagIdList())) {
                log.info("查询用户升级场景为空，不弹窗，忽略请求", uid);
                return res;
            }

            if (CollectionUtil.containsAny(userTags.getTagIdList(), Arrays.asList(TutorChannelSettingEnum.ZI_YING.getValue(),
                    TutorChannelSettingEnum.PANG_E.getValue(), TutorChannelSettingEnum.DUAN_WEI.getValue()))) {
                log.info("用户【{}】升级场景值为自营、胖鹅或断尾，不弹窗，忽略请求", uid);
                return res;
            }

            //判断今天是否已经弹窗过
            boolean isPoppedToday = checkPoppedToday(uid, request.getTaskId());
            if (isPoppedToday) {
                log.info("popup 今天已经弹窗过，忽略请求，uid:{}", uid);
                return res;
            }
            //判断今天是否发过券
            String period = LocalDate.now().toString();
            Long awardTime = getAwardTimeToday(request.getTaskId(), uid, period);
            if (awardTime == null) {
                log.info("popup 今天未发券，忽略请求，uid:{}", uid);
                return res;
            }
            //判断今天发券后，已经超过弹窗时间
            long now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
            if (now - awardTime < registerGiftPopupConfig.getInterval()) {
                log.info("popup 今天发券后，未到5分钟，忽略请求，uid:{}", uid);
                return res;
            }

            //查询任务关联的券金额
            AwardInfoDTO awardInfoDTO = irisActivityTaskService.getAwardInfoByTaskBizId(request.getTaskId());
            if (awardInfoDTO != null) {
                res.setValue(awardInfoDTO.getValue());
            }

            res.setFlag(true);
            setPopTagToday(uid, request.getTaskId());
            log.info("popup 弹窗成功 uid:{}，response{}", uid, JSON.toJSONString(res));
        } catch (Exception e) {
            log.error("popup 执行异常, uid:{}", uid, e);
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return res;
    }

    @Override
    public void setAwardTimeToday(String taskId, String uid, String taskPeriod, LocalDateTime expiredTime) {
        //发券成功，设置发券标识
        String userAwardFlagKey = ActivityRedisKey.getUserAwardTimeKey(uid, taskId, taskPeriod);
        long swardTime = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
        redisClient.set(userAwardFlagKey, String.valueOf(swardTime), "NX", "EX",
                expiredTime.toEpochSecond(ZoneOffset.of("+8")));
    }

    /**
     * 获取今天的发券时间
     *
     * @param taskId
     * @param uid
     * @param taskPeriod
     * @return
     */
    public Long getAwardTimeToday(String taskId, String uid, String taskPeriod) {
        //发券成功，设置发券标识
        String userAwardFlagKey = ActivityRedisKey.getUserAwardTimeKey(uid, taskId, taskPeriod);
        String s = redisClient.get(userAwardFlagKey);
        if (Strings.isNullOrEmpty(s)) {
            return null;
        }
        return Long.parseLong(s);
    }

    /**
     * 检查今天是否已经弹窗
     *
     * @param taskId
     * @param uid
     * @return
     */
    public boolean checkPoppedToday(String uid, String taskId) {
        String userAwardPopupTagKey = ActivityRedisKey.getUserAwardPopupTagKey(uid, taskId);
        return redisClient.exists(userAwardPopupTagKey);

    }

    /**
     * 设置今日已弹窗的标识
     *
     * @param taskId
     * @param uid
     */
    public void setPopTagToday(String uid, String taskId) {
        String userAwardPopupTagKey = ActivityRedisKey.getUserAwardPopupTagKey(uid, taskId);
        redisClient.set(userAwardPopupTagKey, "1", "NX", "EX", 24 * 3600);
    }

    @Override
    public Boolean closePopup(ActivityGiftPopupRequest request) {

        return null;
    }

    @Override
    public Boolean awardConfirm(AwardConfirmRequest request) {
        Boolean awardConfirmOn = Optional.ofNullable(activityConfig.getAwardConfirmOn()).orElse(Boolean.TRUE);
        if (!awardConfirmOn) {
            //关闭发奖校验，返回成功
            return Boolean.TRUE;
        }

        String uid = request.getUid();

        request = Optional.ofNullable(responseMockConfig.getAwardConfirmMock().get(uid))
                .orElse(request);
        String outTradeNo = request.getOutTradeNo();
        String awardId = request.getAwardId();
        String transactionFlag = request.getTransactionFlag();

        TIrisUserTaskRecordDO taskRecord = activityCommonService.queryAwardTaskRecordInfo(uid, outTradeNo);
        if (taskRecord == null) {
            log.error("发奖确认查询任务记录不存在，uid：{}，outTradeNo：{}，awardId：{}，transactionFlag：{}",
                    uid, outTradeNo, awardId, transactionFlag);
            return Boolean.FALSE;
        }

        String taskId = taskRecord.getTaskId();
        AwardInfoDTO awardInfo = irisActivityTaskService.getAwardInfoByTaskBizId(taskId);
        if (awardInfo == null) {
            log.error("发奖确认查询发奖信息不存在，uid：{}，outTradeNo：{}，awardId：{}，transactionFlag：{}",
                    uid, outTradeNo, awardId, transactionFlag);
            return Boolean.FALSE;
        }

        String dbAwardId = awardInfo.getAwardId();
        String dbTransactionFlag = awardInfo.getTransactionFlag();
        AwardInfoDTO.NewAward newAward = Optional.ofNullable(awardInfo.getNewAward())
                .orElseGet(AwardInfoDTO.NewAward::new);
        String newAwardId = newAward.getAwardId();
        if (!Objects.equals(transactionFlag, dbTransactionFlag)) {
            log.error("发奖确认查询场景值不正确，uid：{}，outTradeNo：{}，awardId：{}，transactionFlag：{}，实际transactionFlag：{}",
                    uid, outTradeNo, awardId, transactionFlag, dbTransactionFlag);
            return Boolean.FALSE;
        }

        if (!Objects.equals(dbAwardId, awardId) && !Objects.equals(newAwardId, awardId)) {
            log.error("发奖确认查询奖品id不正确，uid：{}，outTradeNo：{}，awardId：{}，transactionFlag：{}，实际awardId：{}",
                    uid, outTradeNo, awardId, transactionFlag, dbAwardId);
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }


    @Override
    public List<TIrisActivityAwardRecordDO> queryIssueList(String actId,
            String taskId, String uid, Date startDate, Date endDate) {
        TIrisActivityAwardRecordExample example = new TIrisActivityAwardRecordExample();
        example.createCriteria()
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.SHARDING_ID, ShardingIdUtil.doSharding(
                        uid))
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.UID, uid)
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.ACTIVITY_ID, actId)
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.TASK_ID, taskId)
                .andPropertyGreaterThanOrEqualTo(TIrisActivityAwardRecordExample.CREATE_TIME, startDate)
                .andPropertyLessThanOrEqualTo(TIrisActivityAwardRecordExample.CREATE_TIME, endDate)
                .andPropertyIn(TIrisActivityAwardRecordExample.ISSUE_STATUS, Lists.newArrayList(
                        AwardRecordIssueStatusEnum.ISSUE_SUCCEED.getCode(),
                        AwardRecordIssueStatusEnum.ISSUE_UNABLE.getCode()));
        List<TIrisActivityAwardRecordDO> list = activityAwardRecordMapper.selectByExample(example);
        return list;
    }

    @Override
    public TIrisActivityAwardRecordDO queryAwardRecord(String uid, String actId, String taskId, String taskRecordId) {
        TIrisActivityAwardRecordExample example = new TIrisActivityAwardRecordExample();
        example.createCriteria()
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.SHARDING_ID, ShardingIdUtil.doSharding(uid))
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.UID, uid)
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.ACTIVITY_ID, actId)
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.TASK_ID, taskId)
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.TASK_RECORD_ID, taskRecordId)
                .andPropertyEqualTo(TIrisActivityAwardRecordExample.STATUS, EYesNo.NO.getValue())
                .andPropertyIn(TIrisActivityAwardRecordExample.ISSUE_STATUS, Lists.newArrayList(
                        AwardRecordIssueStatusEnum.ISSUE_SUCCEED.getCode(),
                        AwardRecordIssueStatusEnum.ISSUE_UNABLE.getCode()));
        List<TIrisActivityAwardRecordDO> list = activityAwardRecordMapper.limitSelectByExample(example, 0, 1);
        return CollectionUtil.isNotEmpty(list) ? list.get(0) : null;
    }

    @Override
    public void deleteInviteAwardLimitCache(String pid, String uid) {
        log.info("deleteInviteAwardLimitCache pid:{},uid:{}",pid,uid);
        if (StringUtils.isBlank(pid) || StringUtils.isBlank(uid)) {
            return;
        }
        Long becomeTraineeTime = activityCommonService.queryTraineeTime(uid);
        log.info("deleteInviteAwardLimitCache pid:{},uid:{},becomeTraineeTime:{}",pid,uid,becomeTraineeTime);
        if (Objects.isNull(becomeTraineeTime)) {
            return;
        }
        String inviteLimitCacheKey = String.format(ActivityConfigConstants.IRIS_INVITE_UPPER_RECORD_AWARD_LIMIT_CACHE_KEY, pid,
                DateUtil.format(new Date(becomeTraineeTime), "yyyyMM"));
        redisClient.del(inviteLimitCacheKey);
    }

    @Override
    public void incrInviteAwardLimitCache(String pid, String uid, String taskBizId) {
        log.info("incrInviteAwardLimitCache pid:{},uid:{},taskBizId:{}",pid,uid,taskBizId);
        if (StringUtils.isBlank(pid) || StringUtils.isBlank(uid)) {
            return;
        }
        Long becomeTraineeTime = activityCommonService.queryTraineeTime(uid);
        log.info("incrInviteAwardLimitCache pid:{},uid:{},becomeTraineeTime:{}",pid,uid,becomeTraineeTime);
        if (Objects.isNull(becomeTraineeTime)) {
            return;
        }
        String inviteLimitCacheKey = String.format(ActivityConfigConstants.IRIS_INVITE_UPPER_RECORD_AWARD_LIMIT_CACHE_KEY, pid,
                DateUtil.format(new Date(becomeTraineeTime), "yyyyMM"));
        String s = redisClient.get(inviteLimitCacheKey);
        if (StringUtils.isNotBlank(s)) {
            TIrisActivityAwardRecordExample example = new TIrisActivityAwardRecordExample();
            example.createCriteria()
                    .andPropertyEqualTo(TIrisActivityAwardRecordExample.SHARDING_ID, ShardingIdUtil.doSharding(
                            uid))
                    .andPropertyEqualTo(TIrisActivityAwardRecordExample.UID, uid)
                    .andPropertyEqualTo(TIrisActivityAwardRecordExample.ACTIVITY_ID,
                            activityConfig.getInviteGiftActivityId())
                    .andPropertyEqualTo(TIrisActivityAwardRecordExample.TASK_ID
                            , ActivityTaskIdEnum.INVITE_GMV_TASK.getCode())
                    .andPropertyEqualTo(TIrisActivityAwardRecordExample.TASK_RECORD_ID,taskBizId);

            List<TIrisActivityAwardRecordDO> list = activityAwardRecordMapper.selectByExample(example);
            if (CollectionUtils.isEmpty(list)) {
                redisClient.incr(inviteLimitCacheKey);
                return;
            }
            long count = list.stream()
                    .filter(a -> (a.getIssueStatus() == AwardRecordIssueStatusEnum.ISSUE_SUCCEED.getCode()
                            || a.getIssueStatus() == AwardRecordIssueStatusEnum.ISSUE_FAILED.getCode())).count();
            if (count > 0){
                log.info("incrInviteAwardLimitCache uid old pid:{},uid:{},awardRecordList:{}"
                        ,pid,uid,JSON.toJSONString(list));
                return;
            }
            log.info("incrInviteAwardLimitCache cache value:{}",s);
            redisClient.incr(inviteLimitCacheKey);
        }
    }


    /**
     * @param request
     * @return
     */
    public BoolQueryBuilder getCommonQuery(AwardRecordRequest request) {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery(StrUtil.toCamelCase(TIrisActivityAwardRecordExample.AWARD_UID), request.getUid()))
                .must(QueryBuilders.termQuery(TIrisActivityAwardRecordExample.STATUS, EYesNo.NO.getValue()));

        //任务id
        List<ActivityIdEnum> activityIds = request.getActivityIds();
        if (CollectionUtil.isNotEmpty(activityIds)) {
            List<String> ids = activityIds.stream().map(ActivityIdEnum::getCode).collect(Collectors.toList());
            queryBuilder.must(QueryBuilders.termsQuery(StrUtil.toCamelCase(TIrisActivityAwardRecordExample.ACTIVITY_ID), ids));
        }

        //任务参与时间
        if (request.getStartTime() != null && request.getEndTime() != null) {
            queryBuilder.must(QueryBuilders.rangeQuery(StrUtil.toCamelCase(TIrisActivityAwardRecordExample.CREATE_TIME))
                    .gte(request.getStartTime()).lte(request.getEndTime()));
        }

        List<ActivityTaskIdEnum> taskIds = request.getTaskIds();
        if (CollectionUtil.isNotEmpty(taskIds)) {
            List<String> ids = taskIds.stream().map(ActivityTaskIdEnum::getCode).collect(Collectors.toList());
            queryBuilder.must(QueryBuilders.termsQuery(StrUtil.toCamelCase(TIrisActivityAwardRecordExample.TASK_ID), ids));
        }

        List<Byte> awardIssueStatus = request.getAwardIssueStatus();
        if (CollectionUtil.isNotEmpty(awardIssueStatus)) {
            queryBuilder.must(QueryBuilders.termsQuery(StrUtil.toCamelCase(TIrisActivityAwardRecordExample.ISSUE_STATUS), awardIssueStatus));
        }

        return queryBuilder;
    }


}
